<?php
/**
 * User: hunma 2025/2/4 22:17
 */
namespace hunma\utils;

use support\Log;
use think\Exception;
use WhichBrowser\Parser;

class IpUtil
{
    /**
     * 获取客户端 IP 地址
     * 该方法优先考虑通过代理服务器传递的客户端 IP 地址，以适应大多数 Web 应用场景
     *
     * @return string 客户端 IP 地址
     */
    public static function getClientIp(): string
    {
        // 从 X-Forwarded-For 获取
        $ip = request()->header('x-forwarded-for');
        if ($ip) {
            $ips = explode(',', $ip);
            $ip = trim($ips[0]);
        }

        // 如果 X-Forwarded-For 不存在，从 X-Real-IP 获取
        if (empty($ip)) {
            $ip = request()->header('x-real-ip');
        }

        // 如果以上都不存在，直接获取客户端 IP
        if (empty($ip)) {
            $ip = request()->getRemoteIp();
        }
        return $ip;
    }

    /**
     * 根据IP地址获取地理位置
     *
     * 该方法使用ip-api.com的API来确定给定IP地址的地理位置
     * 它主要返回城市和国家信息
     *
     * @return string 登录地址，格式为"城市, 国家"
     */
    public static function getLocation($ip)
    {

        try{
            // 验证 IP 地址的有效性
            if (!filter_var($ip, FILTER_VALIDATE_IP)) {
                throw new \Exception('无效的IP地址');
            }

            // 初始化 cURL 会话
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, "http://ip-api.com/json/{$ip}");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 设置超时时间

            // 执行 cURL 请求并获取响应
            $response = curl_exec($ch);

            // 检查 cURL 请求是否成功
            if ($response === false) {
                curl_close($ch);
                throw new \Exception('获取位置数据失败：' . curl_error($ch));
            }

            // 关闭 cURL 会话
            curl_close($ch);

            // 解码 JSON 数据
            $locationData = json_decode($response, true);

            // 检查 JSON 解码是否成功
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \Exception('JSON数据解码失败： ' . json_last_error_msg());
            }

            // 检查必要的键是否存在
            if (!isset($locationData['city']) || !isset($locationData['country'])) {
                throw new \Exception('位置数据不完整');
            }

            // 获取登录地址
            $loginLocation = $locationData['city'] . ', ' . $locationData['country'];

            // 返回登录地址
            return $loginLocation;
        }catch (\Exception $e){
            Log::error('根据IP地址获取地理位置异常:'.$e->getMessage().",IP:$ip");
            return '未知';
        }

    }


    /**
     * 解析User-Agent字符串
     *
     * 该方法用于验证和解析传入的User-Agent字符串，以生成一个Parser对象
     * 如果User-Agent字符串无效或解析失败，将抛出异常
     *
     * @param string $userAgent 用户代理字符串，用于识别客户端的类型和操作系统等信息
     * @throws \InvalidArgumentException 如果User-Agent字符串为空或不是字符串类型
     * @throws \RuntimeException 如果解析User-Agent字符串时发生错误
     * @return Parser 返回一个Parser对象，用于进一步解析User-Agent
     */
    public static function parseUserAgent($userAgent)
    {
        // 输入验证
        if (!is_string($userAgent) || empty(trim($userAgent))) {
            throw new \InvalidArgumentException('User-Agent 头缺失或为空。');
        }

        try {
            // 尝试实例化 Parser
            return new Parser($userAgent);
        } catch (\Exception $e) {
            // 记录异常日志
            error_log("无法解析 User-Agent: " . $e->getMessage());
            // 重新抛出异常，提供给调用者处理
            return '无法解析 User-Agent。'.$e->getMessage();
        }
    }
    /**
     * 获取并验证 User-Agent 头信息
     *
     * 此方法用于从 HTTP 请求中获取 User-Agent 头信息，并进行简单的验证和过滤
     * 如果 User-Agent 头信息缺失或不符合预期格式，则返回相应的错误信息
     *
     * @return string 返回 User-Agent 头信息，如果验证失败则返回 'Unknown' 或 'Invalid User-Agent'
     */
    public static function getUserAgent()
    {
        // 获取 User-Agent 头
        $userAgent = request()->header('User-Agent', '');

        // 确保 User-Agent 不为 null 并且不是空字符串
        if (empty($userAgent)) {
            return 'Unknown';
        }

        // 进行简单的验证和过滤，防止潜在的安全风险
        // 这里假设我们只允许字母、数字、常见符号和空格
        if (!preg_match('/^[a-zA-Z0-9\-\.\_\*\/\(\)\[\]\{\}\s]+$/', $userAgent)) {
            return 'Invalid User-Agent';
        }

        // 返回验证通过的 User-Agent
        return $userAgent;
    }

}
